
#include "bootload.h"
//	#include "timer.h"
#include "fifo.h"
#include "boot.h"
#include <Log.h>

#define FRM_T_UART		0xA2
#define FRM_T_BLE		0x2A

#pragma pack(1)

typedef struct Frame{
	uint8_t stx;
	uint8_t len[2];
	uint8_t type;
	uint8_t code;
	uint8_t content[];
}Frame_T;

typedef struct Content{
	uint8_t addr[4];
	uint8_t data[];
}Content_T;

#pragma pack()

static inline uint16_t Frame_GetContentLength(const Frame_T* frame){
	return (uint16_t)((uint16_t)frame->len[0] << (uint16_t)8) | (uint16_t)frame->len[1];
}

static inline uint32_t Content_GetAddress(const Content_T* content){
	return (uint32_t)(content->addr[0] << 24)|
		(uint32_t)(content->addr[1] << 16)|
		(uint32_t)(content->addr[2] << 8)|
		(uint32_t)(content->addr[3] << 0);
}

static inline uint32_t Content_DataSum(uint32_t sum, const uint8_t* data, uint32_t length){
	uint32_t i;

	for(i = 0; i < length; i++){
		sum += data[i];
	}

	return sum;
}
/*---------------------------------------------------------------*/
#define RD_ATTR_HF		(1u << 0)
#define RD_ATTR_SE		(1u << 1)
#define RD_ATTR_LF		(1u << 2)
#define RD_ATTR_BT		(1u << 3)
#define RD_ATTR_HIDICLASS	(1u << 4)
#define RD_ATTR_HIDPROX		(1u << 5)

#define SETTING_DEFAULT		RD_ATTR_HF	\
	|RD_ATTR_SE	\
	|RD_ATTR_LF	\
	|RD_ATTR_BT	\
	|RD_ATTR_HIDICLASS	\
	|RD_ATTR_HIDPROX

#define SETTING_ADDR	UP_FLAG_ADDR
#define SETTING_NUN		2

#pragma pack(1)
typedef struct {
	uint32_t data;
	uint32_t check;
}Setting_T;
#pragma pack()

static Setting_T settings[SETTING_NUN];
static const uint32_t settingAddr[SETTING_NUN] = {
	SETTING_ADDR, SETTING_ADDR + SECTOR_SIZE / 2
};
	
static int setting_check(const Setting_T* setting){
	return setting->data == ~ setting->check;
}

static void setting_enc(Setting_T* setting){
	uint32_t uid = boot_get_uid();

	setting->data ^= uid;
	setting->check ^= uid;
}

static void setting_get(uint32_t address, Setting_T* setting) {
	boot_flash_read(address, (uint8_t *)setting, sizeof(Setting_T));
	setting_enc(setting);
}

static int setting_set(uint32_t address, const Setting_T* setting){
	Setting_T s = *setting;
	setting_enc(&s);
	
	boot_flash_write(address, (uint32_t*)&s, sizeof(Setting_T)/sizeof(uint32_t));
	Setting_T s1 = {0};
	boot_flash_read(address, (uint8_t *)&s1, sizeof(Setting_T));

	if(memcmp(&s, &s1, sizeof(s))){
		return -1;
	}
	
	return 0;
}

static int setting_read(Setting_T* setting){

	for(int i = 0; i < SETTING_NUN; i++){
		if(setting_check(&settings[i])){
			*setting = settings[i];
			return i;
		}
	}

	return -1;
}

static int setting_write(uint32_t data){

	for(int i = 0; i < SETTING_NUN; i++){
		settings[i].data = data;
		settings[i].check = ~data;
	}
	
	return 0;
}

static int setting_changed(void){
	Setting_T setting;
	
	for(int i = 0; i < SETTING_NUN; i++){
		setting_get(settingAddr[i],  &setting);
		if(memcmp(&setting, &settings[i], sizeof(Setting_T))){
			return 1;
		}
	}

	return 0;
}

static int setting_check_and_correct(void){
	uint32_t flag = 0;
	
	for(int i = 0; i < SETTING_NUN; i++){
		if(setting_check(&settings[i])){
			flag |= 1 << i;
		}
	}
	
	switch(flag){
	case 1:
	case 3:{
		if(settings[0].data != settings[1].data){
			settings[1] = settings[0];
		}
	}
	break;
	case 2:{
		settings[0] = settings[1];
	}
	break;
	default :{
		return -1;
	}
	}

	return 0;
}

static int setting_update(void){
	boot_flash_erase(SETTING_ADDR, SECTOR_SIZE / SECTOR_SIZE);
	for(int i = 0; i < SETTING_NUN; i++){
		if(setting_set(settingAddr[i], &settings[i])){
			return -1;
		}
	}
	
	return 0;
}

static int setting_flush(void){
	if(setting_changed()){
		if(setting_check_and_correct()){
			return -1;
		}
		return setting_update();
	}
	
	return 0;
}

static void setting_init(void){
	for(int i = 0; i < SETTING_NUN; i++){
		setting_get(settingAddr[i], &settings[i]);
	}
	if(setting_check_and_correct()){
		setting_write(SETTING_DEFAULT);
		setting_update();
	}
}

static int command_write_setting(uint32_t data){
	setting_write(data);
	return setting_flush();
}

static int command_read_setting(uint32_t* data){
	int len;
	Setting_T setting;
	int ret = setting_read(&setting);
	if(ret < 0){
		len = 0;
	}
	else{
		if(ret > 0){
			setting_check_and_correct();
			setting_update();
		}
		memcpy(data, &setting.data, sizeof(uint32_t));
		len = sizeof(uint32_t);
	}
	
	return len;
}

/*---------------------------------------------------------------*/
static uint32_t flash_size;
static uint32_t flash_end;
static uint8_t  fifo_buf[1024];
static fifo_t   fifo;
//	static Timer_T  timer;
static volatile uint32_t frameTime = 0;
static volatile uint32_t idleTime = 0;
//	static uint32_t upgrade_flag;
static image_head_t image_head;
static uint8_t cmdBuf[CMD_BUFF_SIZE];
static uint8_t uartTxBuff[CMD_BUFF_SIZE];
static uint32_t aprom_addr =0, ap_checksum =0;
static int commandFrameCheck(uint8_t *px, uint16_t px_len);
static void commandFrameReply(uint8_t channel, uint8_t *px, uint16_t len, uint8_t cmd);
void aprom_encrypt(uint8_t *pbuf, uint32_t len);
static int commandHandle(uint8_t *cmd, uint32_t len);

static volatile uint32_t bleTime;
static uint8_t bleState;

static int bootload_ble_cmd(uint8_t* cmd, uint8_t len, uint8_t *resp, uint8_t* rlen, uint32_t wtime){
	uint8_t index = 0;
	uint8_t size = *rlen;
	bleTime = wtime;
	Fifo_Clean(&fifo);
	boot_ble_send(cmd, len);
	while(bleTime > 0){
		uint32_t slen = Fifo_GetBlock(&fifo, resp + index, size - index);
		index += slen;
		if(resp[index - 1] == 0xA5){
			*rlen = index;
			return 0;
		}
	}
	*rlen = index;
	return 1;
}

static void bootload_ble_init(void){
	//打开广播
	uint8_t cmd[] = {0x5A, 0x02, 0x01, 0x01, 0x02, 0xA5};
	uint8_t resp[16] = {0};
	uint8_t rlen = sizeof(resp);
	bootload_ble_cmd(cmd, sizeof(cmd), resp, &rlen, 20);
}

static void bootload_ble_deinit(void){
{
	//断开连接
	uint8_t cmd[] = {0x5A, 0x01, 0x03, 0x02, 0xA5};
	uint8_t resp[16] = {0};
	uint8_t rlen = sizeof(resp);
	bootload_ble_cmd(cmd, sizeof(cmd), resp, &rlen, 20);
}

{
	//停止广播
	uint8_t cmd[] = {0x5A, 0x02, 0x01, 0x00, 0x03, 0xA5};
	uint8_t resp[16] = {0};
	uint8_t rlen = sizeof(resp);
	bootload_ble_cmd(cmd, sizeof(cmd), resp, &rlen, 20);
}

}

void bootload_init(void)
{
	TRACE_I("start!");
	idleTime = 3000;
	bleTime = 0;
	bleState = 0;
	flash_size = boot_flash_size();
	flash_end = flash_size + FLASH_BASE;
	Fifo_Init(&fifo, fifo_buf, sizeof(fifo_buf));
	
	boot_init();
	setting_init();
}

void bootload_task(void)
{
	static uint32_t index = 0;
	uint32_t len = 0;
	
	if(index && frameTime == 0){
		TRACE_E("rx timeout!");
		LOG_BUFS("cmdBuf", cmdBuf, index);
		index = 0;
	}
	
	if(index >= sizeof(cmdBuf)){
		index = 0;
		Fifo_Clean(&fifo);
	}
	
	if((len = Fifo_GetBlock(&fifo, cmdBuf + index, sizeof(cmdBuf) - index)) != 0){
		index += len;
		int ret = commandFrameCheck(cmdBuf, (uint16_t)index);
		if(!ret){//检查数据帧是否正确
//				LOG_BUFS("cmd", cmdBuf, index);
			ret = commandHandle(cmdBuf, index);
			if(!ret){
				idleTime = 5000;
			}
			frameTime = index = 0;
			
		} else {
			if(ret < 0){
				//数据错乱，清空缓存
				index = 0;
				Fifo_Clean(&fifo);
			}
#if 0
			TRACE_D("rx more");
#endif 
			//数据可能未接受完整,继续接收数据
			frameTime = 2000;//5000;//设置超时时间 qch
		}
	}
	
	if(idleTime == 0)//上电3s检测升级命令
	{
		if(bleState){
			if(boot_app_check(APP_ADDRESS)){
				TRACE_D("ble deinit!");
				bootload_ble_deinit();
			}
		}
		
		TRACE_D("jump to app");
#ifndef NDEBUG 
		frameTime = 10;
		while(frameTime);
#endif 
		
		boot_run_app(APP_ADDRESS);
		if(!bleState){
			TRACE_D("ble init!");
			bootload_ble_init();
			bleState = 1;
		}
		idleTime = 3000;
	}
}

static int commandHandle(uint8_t *cmd, uint32_t len)
{
	Frame_T* frame = (Frame_T*)cmd;
	uint8_t code = cmd[4];
	uint8_t channel = frame->type;
	
	switch(code) //命令
	{
		case 0x16:
		case 0x06:	//下发升级指令
		{
			LOGD("cmd", "upgrade(%02X)", code);
			cmd[0] = 0;
			commandFrameReply(channel, (uint8_t *)cmd, 1, code+1);
		}
		break;

		case 0x49:  //查询单片机状态
		{
			LOGD("cmd", "get version(%02X)", code);
			memcpy((uint8_t *)cmd, (uint8_t *)LD_BL_VER, 3);
			uint8_t uid[8];
			memcpy(uid, (void*)0x1FFFF7EC, sizeof(uid));
			memcpy((uint8_t *)&cmd[3], uid, 8);
			cmd[10] = 0x01;
			commandFrameReply(channel, (uint8_t *)cmd, 11, 0x50);
		}
		break;
		case 0x59:  //查询单片机状态
		{
			LOGD("cmd", "get version(%02X)", code);
			memcpy((uint8_t *)cmd, (uint8_t *)LD_BL_VER, 3);
			uint8_t uid[8];
			memcpy(uid, (void*)0x1FFFF7EC, sizeof(uid));
			memcpy((uint8_t *)&cmd[3], uid, 8);
			cmd[10] = 0x01;
			commandFrameReply(channel, (uint8_t *)cmd, 11, 0x60);
		}
		break;
		case 0x61:
		case 0x51:  //擦除flash        
		{
			LOGD("cmd", "erase(%02X)", code);
			
			cmd[0] = (uint8_t)boot_flash_erase(APP_ADDRESS, (flash_end - APP_ADDRESS) / SECTOR_SIZE);
			LOGD("cmd", "erase: %d", cmd[0]);
			commandFrameReply(channel, (uint8_t *)cmd, 1, code + 1);
			aprom_addr = APP_ADDRESS; ap_checksum =0;
		}
		break;
		case 0x63:
		case 0x53:   //升级数据
		{
			LOGD("cmd", "write(%02X)", code);
			uint16_t length = (uint16_t)(len - 6);
			if(length  && aprom_addr < flash_end)
			{
				for(uint16_t i=0; i< length;i++)
					ap_checksum += cmd[5+i];

				LOGD(NULL, "ap_checksum = %08X", ap_checksum );
				aprom_encrypt(&cmd[5], length); 
				if(aprom_addr == APP_ADDRESS){
					memcpy(&image_head, &cmd[5], sizeof(image_head));
					memset(&cmd[5], 0xFF, sizeof(image_head));
				}
//					boot_flash_write(aprom_addr, (uint32_t *)&cmd[5], length / 4);
				if(boot_flash_write_data(aprom_addr, &cmd[5], length)){
					cmd[0] = 2;
				}
				else{
					cmd[0] = 0;
				}
				aprom_addr += length;
			}
			else 
			{
				cmd[0] =1;	length =0; aprom_addr =0; ap_checksum =0;
			}
			cmd[1] = length>>8;	cmd[2] = (uint8_t)length;
			LOGD("cmd", "write status: %d", cmd[0]);
			commandFrameReply(channel, (uint8_t *)cmd, 3, code + 1);
		}
		break;
		case 0x69:{
			LOGD("cmd", "write(%02X)", code);
			uint16_t clen = Frame_GetContentLength(frame);
			Content_T *content = (Content_T*)frame->content;
			uint32_t addr = Content_GetAddress(content);
			addr += APP_ADDRESS;
			if(clen  && addr < flash_end)
			{
				uint16_t dlen = clen - sizeof(content->addr);
				uint32_t sum = Content_DataSum(ap_checksum, content->data, dlen);
//					ap_checksum = Content_DataSum(ap_checksum, content->data, dlen);
				LOGD(NULL, "ap_checksum = %08X", sum );
				aprom_encrypt(content->data, dlen);
				
				if(addr == APP_ADDRESS){
					memcpy(&image_head, content->data, sizeof(image_head));
					memset(content->data, 0xFF, sizeof(image_head));
				}
				cmd[0] = 0;
				if(!boot_flash_verify(addr, content->data, dlen)){
					//
					ap_checksum = sum;
//						boot_flash_write(addr, (uint32_t *)content->data, dlen / 4);
					if(boot_flash_write_data(addr, content->data, dlen)){
						cmd[0] = 2;
					}
				}
				else{
					if(aprom_addr < addr){
						ap_checksum = sum;
					}
				}
				aprom_addr = addr + dlen;
			}
			else 
			{
				cmd[0] =1;	clen =0; aprom_addr =0; ap_checksum =0;
			}
			cmd[1] = clen>>8;	cmd[2] = (uint8_t)clen;
			commandFrameReply(channel, (uint8_t *)cmd, 3, code + 1);
		}
		break;
		case 0x65:
		case 0x55:   //完成升级	
		{
			uint8_t status = 1;
			LOGD("cmd", "check&run(%02X)", code);
			if(boot_flash_sync()){
				status = 3;
			}
			else
			if(aprom_addr)
			{
				uint32_t addr,chsum=0;
				uint8_t tmp[sizeof(image_head_t)] = {0};
				
				memcpy(tmp, &image_head, sizeof(image_head));
				aprom_encrypt(tmp, sizeof(image_head)); //加密
				for(int i = 0; i < sizeof(image_head); i++){
					chsum += tmp[i];
				}
				
				chsum += boot_flash_calc_enc_sum(APP_ADDRESS+sizeof(image_head), aprom_addr);
				if(chsum == ap_checksum && (ap_checksum&0xff) == cmd[5])
				{
					status = 0;
					LOGD("OK", "ap_checksum = %08X, chsum = %08X, sum = %08X",
						ap_checksum, chsum, cmd[5]);
					if(boot_check_image(&image_head)){
						boot_flash_write(APP_ADDRESS, (uint32_t*)&image_head, sizeof(image_head)/sizeof(uint32_t));
					}
					else{ 
						status = 3;
						LOGE("ERR", "msp = %08X, reset_handler = %08X",
							image_head.msp, image_head.reset_handler);
					}
				}
				else
				{
					status = 2; 																		
					LOGE("ERR", "ap_checksum = %08X, chsum = %08X, sum = %08X",
						ap_checksum, chsum, cmd[5]);
				}
			}
			else{
				LOGE("ERR", "aprom_addr == 0!");
			}
			LOGD("cmd", "check&run: %d", status);
			commandFrameReply(channel, &status, 1, code + 1);
			if(!status)
			{
				TRACE_D("ble deinit!");
				bootload_ble_deinit();
				//等待应答发送完成
				frameTime = 10;
				while(frameTime != 0){
					
				}
				boot_run_app(APP_ADDRESS);
			}
		}
		break;

		case 0xF1:{
			LOGD("cmd", "read_setting");
			uint32_t data;
			uint32_t rlen = (uint32_t)command_read_setting(&data);
			if(rlen != sizeof(data)){
				cmd[0] = 1;
				rlen  = 1;
				LOGE("ERR", "read_setting fail!");
			}
			else{
				cmd[0] = 0;
				memcpy(&cmd[1], &data, rlen);
				rlen = sizeof(data) + 1;
				LOGD("OK", "setting=%08X", data);
			}

			commandFrameReply(channel, cmd, (uint16_t)rlen, 0xF2);
		}
		break;

		case 0xF3:{
			LOGD("cmd", "write_setting");
			uint32_t data;
			uint16_t clen = Frame_GetContentLength(frame);
			if(clen != 4){
				LOGE("ERR", "clen=%d!", clen);
				cmd[0] = 2;
				commandFrameReply(channel, cmd, 1, 0xF4);
				break;
			}
			
			memcpy(&data, frame->content, 4);
			int ret = command_write_setting(data);
			if(ret){
				cmd[0] = 1;
				LOGD("ERR", "setting=%08X", data);
			}
			else{
				cmd[0] = 0;
				LOGD("OK", "setting=%08X", data);
			}
			commandFrameReply(channel, cmd, 1, 0xF4);
		}
		break;
		
		default :{
			commandFrameReply(channel, (uint8_t *)cmd, 1, 0xFF);
			return -1;
		}
	}
	return 0;
}

static void commandFrameReply(uint8_t channel, uint8_t *px, uint16_t len, uint8_t cmd)
{
	uint8_t i;
	
	uartTxBuff[0] = UART_SOF;
	uartTxBuff[1] = (uint8_t)(len>>8);
	uartTxBuff[2] = (uint8_t)len;
	uartTxBuff[4] = cmd;
	for(i = 0; i < len; i++)
	{
		 uartTxBuff[i+5] = px[i];
	}
	uartTxBuff[len+5] = UART_EOF;
	
	if(channel == FRM_T_UART){
		uartTxBuff[3] = 0xA1;
		boot_uart_send(uartTxBuff, len + 6);
	}
	else{
		uartTxBuff[3] = 0x2B;
		boot_ble_send(uartTxBuff, len + 6);
	}
}


static int commandFrameCheck(uint8_t *px, uint16_t px_len)
{
	if(px[0] != UART_SOF){
		//数据错乱
		return -1;
	}
	
	if(px_len <= 5){
		return 1;
	}
	
	const Frame_T* frame = (Frame_T*)px;
	uint16_t clen = Frame_GetContentLength(frame);
	if(clen > 2000){
		//数据错乱
		return -2;
	}
	
	if(px[px_len-1] != UART_EOF){
		return 2;
	}
	
	if(clen != (px_len - 6)){
		return 4;
	}
	//A2 串口指令，2A 蓝牙指令
	if((frame->type != FRM_T_UART) 
		&& (frame->type != FRM_T_BLE)){
		return 5;
	}
	
	return 0;
}

#if 0
static uint8_t commandFrameCheck(uint8_t *px, uint8_t px_len)
{
	uint8_t  *ps = (uint8_t *)px;
	uint16_t plen = (ps[1]<<8) + ps[2];
	
	if( px_len > 5 && UART_SOF == *ps && UART_EOF == *(ps+px_len-1))  
	{
		if((plen+6) == px_len)
		{
			if(0xA2 == ps[3])
			return 0;
		}
	}
	return 1;
}
#endif 

//加密解密
void aprom_encrypt(uint8_t *pbuf, uint32_t len)
{
	for(uint32_t i=0;i<len;i++)
	{
		pbuf[i] ^= 0x86;
	}
}

void bootload_uart_recv(uint16_t byte)
{
	uint8_t c = byte;
	Fifo_PutOne(&fifo, &c);
}

void bootload_ble_recv(uint16_t byte)
{
	uint8_t c = byte;
	Fifo_PutOne(&fifo, &c);
}

void bootload_timer(void)
{
//		if(idleTime < 0xFFFFFFFF) idleTime ++;
	if(idleTime > 0) {
		idleTime--;
	}

	if(frameTime) frameTime --;
	if(bleTime > 0){
		bleTime --;
	}
}

